package cn.yunyichina.provider.pay.service;

import cn.yunyichina.provider.framework.dubbo.service.PacityService;
import cn.yunyichina.provider.framework.dubbo.service.WechatPayService;
import cn.yunyichina.provider.iface.entity.base.OrderExtVo;
import cn.yunyichina.provider.iface.entity.common.ResCommon;
import cn.yunyichina.provider.iface.entity.common.Response;
import cn.yunyichina.provider.iface.entity.common.ResponseObject;
import cn.yunyichina.provider.iface.entity.dto.pacity.PacityAccessTokenRequestDTO;
import cn.yunyichina.provider.iface.entity.dto.pacity.PacityAccessTokenResponseDTO;
import cn.yunyichina.provider.iface.entity.dto.pay.AlipayOrderCancelRequestDTO;
import cn.yunyichina.provider.iface.entity.dto.pay.AlipayOrderQueryRequestDTO;
import cn.yunyichina.provider.iface.entity.dto.pay.WechatOrderQueryRequestDTO;
import cn.yunyichina.provider.iface.entity.dto.pay.WechatOrderReverseRequestDTO;
import cn.yunyichina.provider.iface.entity.pay.PayCode;
import cn.yunyichina.provider.pay.constant.MethodRouter;
import cn.yunyichina.provider.pay.constant.ResponseEnum;
import cn.yunyichina.provider.pay.entity.PaymentSetting;
import cn.yunyichina.provider.pay.exception.PaySettingNotFoundException;
import cn.yunyichina.provider.pay.service.impl.PaymentSettingServiceImpl;
import cn.yunyichina.utils.commons.DateUtils;
import cn.yunyichina.utils.commons.RandomUtil;
import cn.yunyichina.utils.convert.JsonUtils;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Resource;
import javax.validation.ConstraintViolation;
import java.util.*;

/**
 * Created by Lullaby on 2016/10/27.
 */
public class BaseService {

    private static final Logger logger = LoggerFactory.getLogger(BaseService.class);

    @Resource
    private Properties yaml;

    @Resource
    private PaymentSettingServiceImpl paymentSettingService;

    protected ResponseObject invalidPayParamsResponseObject() {
        return new ResponseObject().buildResultCodeAndMessage(
                ResponseEnum.PAY_INVALID_PARAMETERS.getCode(),
                ResponseEnum.PAY_INVALID_PARAMETERS.getMessage()
        );
    }

    protected ResponseObject invalidRefundParamsResponseObject() {
        return new ResponseObject().buildResultCodeAndMessage(
                ResponseEnum.REFUND_INVALID_PARAMETERS.getCode(),
                ResponseEnum.REFUND_INVALID_PARAMETERS.getMessage()
        );
    }

    protected ResponseObject invalidParamsResponseObject() {
        return new ResponseObject().buildResultCodeAndMessage(
                ResponseEnum.Common.INVALID.getCode(),
                ResponseEnum.Common.INVALID.getMessage()
        );
    }

    protected Response invalidPayParamsResponse() {
        return new Response().buildResultCodeAndMessage(
                ResponseEnum.PAY_INVALID_PARAMETERS.getCode(),
                ResponseEnum.PAY_INVALID_PARAMETERS.getMessage()
        );
    }

    protected String getConfig(String key) {
        return yaml.getProperty(key);
    }

    protected PaymentSetting getPaymentSetting(String appCode, String orgCode, short payMode) throws PaySettingNotFoundException {
        return paymentSettingService.queryPaymentSettingByAppcodeAndOrgcodeAndPaymode(appCode, orgCode, payMode);
    }

    protected PaymentSetting getPaymentSetting(String appId) throws PaySettingNotFoundException {
        return paymentSettingService.queryPaymentSettingByAppId(appId);
    }

    protected PaymentSetting getPaymentSettingByParentId(String parentId) throws PaySettingNotFoundException {
        return paymentSettingService.getPaymentSettingByParentId(parentId);
    }

    protected PaymentSetting getSubPaymentSettingByParentIdAndOrgCodeAndPayMode(String parentId, String orgCode, int payMode) throws PaySettingNotFoundException {
        return paymentSettingService.getSubPaymentSettingByParentIdAndOrgCodeAndPayMode(parentId, orgCode, payMode);
    }

    protected OrderExtVo getBaseOrderByOrderNo(String orderNo) {
        logger.info("BaseService.getBaseOrderByOrderNo.orderNo -> {}", orderNo);

        Map<String, String> orderParams = new HashMap<>();
        orderParams.put("order_no", orderNo);

        Response response = cn.yunyichina.provider.framework.dubbo.service.BaseService.callUnderlineFromInternal(
                MethodRouter.BASE_ORDER_QUERY,
                JsonUtils.toJsonString(orderParams)
        );
        logger.info("BaseService.getBaseOrderByOrderNo.response -> {}", JsonUtils.toJsonString(response));

        if (response.isSuccess()) {
            OrderExtVo orderExtVo = JsonUtils.parseObject(response.getResult(), OrderExtVo.class);

            if (orderExtVo != null) {
                logger.info("BaseService.getBaseOrderByOrderNo.orderExtVo -> {}", JsonUtils.toJsonString(orderExtVo));
            } else {
                throw new RuntimeException("BaseService.getBaseOrderByOrderNo -> [Order not found]");
            }

            return orderExtVo;
        } else {
            throw new RuntimeException("BaseService.getBaseOrderByOrderNo -> [Order query exception]");
        }
    }

    /**
     * 用pay_code换取pay_mode
     *
     * @param payCode
     * @return
     */
    protected int getPayModeByPayCode(String payCode) {
        return PayCode.valueOf(payCode).getMode();
    }

    /**
     * 构建微信医疗支付获取ACCESS_TOKEN入参
     *
     * @param appId
     * @param agentAppId
     * @param agentAppSecret
     * @return
     */
    protected Map<String, String> buildMedicalPayAccessTokenParametersMap(String appId, String agentAppId, String agentAppSecret) {
        Map<String, String> accessTokenParamsMap = new HashMap<>();

        accessTokenParamsMap.put("grant_type", "client_credential");
        accessTokenParamsMap.put("appid", appId);
        accessTokenParamsMap.put("agentappid", agentAppId);
        accessTokenParamsMap.put("agentsecret", agentAppSecret);

        return accessTokenParamsMap;
    }

    /**
     * 获取微信医疗支付接口调用凭证
     *
     * @param params
     * @return
     */
    protected String getWechatMedicalPayAccessToken(Map<String, String> params) {
        logger.info("BaseService.getWechatMedicalPayAccessToken.params -> {}", params);

        try {
            String accessTokenParamsJson = JsonUtils.toJsonString(params);
            logger.info("BaseService.getWechatMedicalPayAccessToken.accessTokenParamsJson -> {}", accessTokenParamsJson);
            ResponseObject accessTokenResponse = WechatPayService.callUnderlineFromInternalObject(
                    MethodRouter.WECHAT_MEDICAL_ACCESS_TOKEN, accessTokenParamsJson
            );

            logger.info("BaseService.getWechatMedicalPayAccessToken.accessTokenResponse -> {}", JsonUtils.toJsonString(accessTokenResponse));
            if (accessTokenResponse.isSuccess()) {
                Map<String, String> accessTokenResultMap = JsonUtils.parseObject(accessTokenResponse.getResultObject(), Map.class);

                return accessTokenResultMap.get("access_token");
            }
        } catch (Exception e) {
            logger.error("BaseService.getWechatMedicalPayAccessToken.Exception", e);
        }

        return null;
    }

    /**
     * 获取平安开放平台授权访问令牌
     *
     * @return
     */
    String getPacityAccessToken() throws Exception {
        PacityAccessTokenRequestDTO accessTokenRequestDTO = new PacityAccessTokenRequestDTO();
        accessTokenRequestDTO.setGrantType("client_credentials");
        accessTokenRequestDTO.setScope("city.pay.simple");

        ResCommon<PacityAccessTokenResponseDTO> response = PacityService.getPacityClient().getPacityAccessToken(accessTokenRequestDTO);
        logger.info("BaseService.getPacityAccessToken.response -> {}", JSON.toJSONString(response));

        String accessToken;
        if (response.isSuccess()) {
            accessToken = response.getResult().getAccessToken();
        } else {
            throw new Exception("Pacity access token exception");
        }
        logger.info("BaseService.getPacityAccessToken.accessToken -> {}", accessToken);

        return accessToken;
    }

    protected <T> List<String> getValidationMessage(Set<ConstraintViolation<T>> constraintViolationSet) {
        if (constraintViolationSet.isEmpty()) {
            return null;
        }

        List<String> result = new ArrayList<>();
        for (ConstraintViolation violation : constraintViolationSet) {
            result.add(violation.getPropertyPath() + " " + violation.getMessage());
        }

        return result;
    }

    protected WechatOrderQueryRequestDTO buildQueryWechatOrderParameters(Map<String, String> params) throws PaySettingNotFoundException {
        String appid = params.get("app_id");

        PaymentSetting paymentSetting = getPaymentSetting(appid);

        WechatOrderQueryRequestDTO queryRequest = new WechatOrderQueryRequestDTO();
        queryRequest.setAppid(appid);
        queryRequest.setMchId(paymentSetting.getMerchantAccount());
        queryRequest.setApiSecret(paymentSetting.getPayKey());
        queryRequest.setTransactionId(params.get("pay_order_no"));
        queryRequest.setOutTradeNo(params.get("order_no"));
        queryRequest.setNonceStr(RandomUtil.getRandomALLChar(32));

        return queryRequest;
    }

    protected WechatOrderReverseRequestDTO buildReverseWechatOrderParameters(Map<String, String> params) throws PaySettingNotFoundException {
        String appid = params.get("app_id");

        PaymentSetting paymentSetting = getPaymentSetting(appid);

        WechatOrderReverseRequestDTO reverseRequest = new WechatOrderReverseRequestDTO();
        reverseRequest.setAppid(appid);
        reverseRequest.setMchId(paymentSetting.getMerchantAccount());
        reverseRequest.setApiSecret(paymentSetting.getPayKey());
        reverseRequest.setTransactionId(params.get("pay_order_no"));
        reverseRequest.setOutTradeNo(params.get("order_no"));
        reverseRequest.setNonceStr(RandomUtil.getRandomALLChar(32));

        return reverseRequest;
    }

    protected AlipayOrderQueryRequestDTO buildQueryAlipayOrderParameters(Map<String, String> params) throws PaySettingNotFoundException {
        String appid = params.get("app_id");

        PaymentSetting paymentSetting = getPaymentSetting(appid);
        String signType = "RSA";
        if (paymentSetting.getPayPublicKey().length() > 256) {
            signType = "RSA2";
        }

        AlipayOrderQueryRequestDTO queryRequest = new AlipayOrderQueryRequestDTO();
        queryRequest.setPayPublicKey(paymentSetting.getPayPublicKey());
        queryRequest.setPublicKey(paymentSetting.getPublicKey());
        queryRequest.setPrivateKey(paymentSetting.getPrivateKey());

        queryRequest.setAppId(paymentSetting.getPayAppId());
        queryRequest.setMethod("alipay.trade.query");
        queryRequest.setFormat("JSON");
        queryRequest.setCharset("utf-8");
        queryRequest.setSignType(signType);
        queryRequest.setTimestamp(DateUtils.dateToString(Calendar.getInstance().getTime()));
        queryRequest.setVersion("1.0");

        Map<String, String> bizContent = new HashMap<>();
        bizContent.put("out_trade_no", params.get("order_no"));
        bizContent.put("trade_no", params.get("pay_order_no"));

        queryRequest.setBizContent(JSON.toJSONString(bizContent));

        return queryRequest;
    }

    protected AlipayOrderCancelRequestDTO buildCancelAlipayOrderParameters(Map<String, String> params) throws PaySettingNotFoundException {
        String appid = params.get("app_id");

        PaymentSetting paymentSetting = getPaymentSetting(appid);
        String signType = "RSA";
        if (paymentSetting.getPayPublicKey().length() > 256) {
            signType = "RSA2";
        }

        AlipayOrderCancelRequestDTO cancelRequest = new AlipayOrderCancelRequestDTO();
        cancelRequest.setPayPublicKey(paymentSetting.getPayPublicKey());
        cancelRequest.setPublicKey(paymentSetting.getPublicKey());
        cancelRequest.setPrivateKey(paymentSetting.getPrivateKey());

        cancelRequest.setAppId(paymentSetting.getPayAppId());
        cancelRequest.setMethod("alipay.trade.cancel");
        cancelRequest.setFormat("JSON");
        cancelRequest.setCharset("utf-8");
        cancelRequest.setSignType(signType);
        cancelRequest.setTimestamp(DateUtils.dateToString(Calendar.getInstance().getTime()));
        cancelRequest.setVersion("1.0");

        Map<String, String> bizContent = new HashMap<>();
        bizContent.put("out_trade_no", params.get("order_no"));
        bizContent.put("trade_no", params.get("pay_order_no"));

        cancelRequest.setBizContent(JSON.toJSONString(bizContent));

        return cancelRequest;
    }

}
